﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Bymed.SpineScan.DataModel;
using Bymed.SpineScan.Service;
using Bymed.SpineScan.Service.Interface;
using Bymed.SpineScan.Service.Shared;
using Bymed.SpineScan.WPF.Model;
using Bymed.SpineScan.WPF.Shared;
using Bymed.SpineScan.WPF.ViewModel.TestItemViewModel;
using Bymed.SpineScan.DataModel.TestItem;

namespace Bymed.SpineScan.WPF.ViewModel
{
    public class PatientDataSearchViewModel : MyViewModelBase
    {
        public const string GoToNextPageMessageToken = "PatientDataSearchGoToNextPageMessage";
        public const string DeleteDataConfirmMessageToken = "PatientDataSearchDeleteDataConfirm";
        public IEnumerable<CustomizedTestItem> AllCustomizedTestItems { get; private set; }
        private IPatientService _patientService;
        private ITestTrackingService _testTrackingService;
        private int _selectedTestDateType;

        /// <summary>
        /// Initializes a new instance of the PatientDataSearchViewModel class.
        /// </summary>
        public PatientDataSearchViewModel(IDataService dataService)
            : base(dataService)
        {
            _patientService = ServiceFactory.CreatePatientService();
            _testTrackingService = ServiceFactory.CreateTestTrackingService();
        }

        public override void ResetProperties()
        {
            base.ResetProperties();

            SelectedPatient = null;
            PatientNumber = string.Empty;
            PatientName = string.Empty;
            PatientList.Clear();
            TestResultList.Clear();

            AllCustomizedTestItems = ServiceFactory.CreateCustomizedTestItemService().GetAll().OrderBy(p => p.OrderIndex);
        }

        /// <summary>
        /// The <see cref="PatientNumber" /> property's name.
        /// </summary>
        public const string PatientNumberPropertyName = "PatientNumber";

        private string _patientNumber = string.Empty;

        /// <summary>
        /// Sets and gets the PatientNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string PatientNumber
        {
            get
            {
                return _patientNumber;
            }

            set
            {
                if (_patientNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(PatientNumberPropertyName);
                _patientNumber = value;
                RaisePropertyChanged(PatientNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="PatientName" /> property's name.
        /// </summary>
        public const string PatientNamePropertyName = "PatientName";

        private string _patientName = string.Empty;

        /// <summary>
        /// Sets and gets the PatientName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string PatientName
        {
            get
            {
                return _patientName;
            }

            set
            {
                if (_patientName == value)
                {
                    return;
                }

                RaisePropertyChanging(PatientNamePropertyName);
                _patientName = value;
                RaisePropertyChanged(PatientNamePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedPatient" /> property's name.
        /// </summary>
        public const string SelectedPatientPropertyName = "SelectedPatient";

        private PatientBasicInfoDisplayViewModel _selectedPatient = new PatientBasicInfoDisplayViewModel(null);

        /// <summary>
        /// Sets and gets the SelectedPatient property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public PatientBasicInfoDisplayViewModel SelectedPatient
        {
            get
            {
                return _selectedPatient;
            }

            set
            {
                if (_selectedPatient == value)
                {
                    return;
                }

                RaisePropertyChanging(SelectedPatientPropertyName);
                _selectedPatient = value;
                RaisePropertyChanged(SelectedPatientPropertyName);

                try
                {
                    if (value != null)
                    {
                        LoadTestResult();
                    }
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            }
        }

        /// <summary>
        /// The <see cref="PatientList" /> property's name.
        /// </summary>
        public const string PatientListPropertyName = "PatientList";

        private ObservableCollection<PatientBasicInfoDisplayViewModel> _patientList = new ObservableCollection<PatientBasicInfoDisplayViewModel>();

        /// <summary>
        /// Sets and gets the PatientList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<PatientBasicInfoDisplayViewModel> PatientList
        {
            get
            {
                return _patientList;
            }

            set
            {
                if (_patientList == value)
                {
                    return;
                }

                RaisePropertyChanging(PatientListPropertyName);
                _patientList = value;
                RaisePropertyChanged(PatientListPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="TestResultList" /> property's name.
        /// </summary>
        public const string TestResultListPropertyName = "TestResultList";

        private ObservableCollection<AllTestResultsViewModel> _testResultList = new ObservableCollection<AllTestResultsViewModel>();

        /// <summary>
        /// Sets and gets the TestResultList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<AllTestResultsViewModel> TestResultList
        {
            get
            {
                return _testResultList;
            }

            set
            {
                if (_testResultList == value)
                {
                    return;
                }

                RaisePropertyChanging(TestResultListPropertyName);
                _testResultList = value;
                RaisePropertyChanged(TestResultListPropertyName);
            }
        }

        private RelayCommand<int> _searchCommand;

        /// <summary>
        /// Gets the SearchCommand.
        /// </summary>
        public RelayCommand<int> SearchCommand
        {
            get
            {
                return _searchCommand
                    ?? (_searchCommand = new RelayCommand<int>(ExecuteSearchCommand));
            }
        }

        private void ExecuteSearchCommand(int selectedTestDateType)
        {
            PatientList.Clear();
            _selectedTestDateType = selectedTestDateType;

            var patient = new Patient
            {
                Name = PatientName,
                Number = PatientNumber
            };

            var patients = _patientService.SearchByTestDateType(patient, (TestDateType)_selectedTestDateType);
            PatientBasicInfoDisplayViewModel patientViewModel = null;

            for (int i = 0; i < patients.Count; i++)
            {
                patientViewModel = CreatePatientBasicInfoDisplayViewModel(patients[i]);
                patientViewModel.SequenceNumber = i + 1;
                PatientList.Add(patientViewModel);
            }

            TestResultList.Clear();
        }

        /// <summary>
        /// Create an instance of PatientBasicInfoDisplayViewModel based on provided patient.
        /// </summary>
        /// <param name="patient">Instance of Patient that contains data from data source.</param>
        /// <returns>An instance of PatientBasicInfoDisplayViewModel</returns>
        private PatientBasicInfoDisplayViewModel CreatePatientBasicInfoDisplayViewModel(Patient patient)
        {
            return new PatientBasicInfoDisplayViewModel(null)
            {
                Number = patient.Number,
                Name = patient.Name,
                GenderDisplay = GeneralUtility.GetGenderDescription(patient.Gender),
                IsMale = GeneralUtility.IsMale(patient.Gender),
                IsFemale = GeneralUtility.IsFemale(patient.Gender),
                Birthday = patient.Birthday,
                BirthdayDisplay = GeneralUtility.DisplayBirthday(patient.Birthday),
                Height = patient.Height.HasValue ? patient.Height.Value.ToString() : string.Empty,
                Weight = patient.Weight.HasValue ? patient.Weight.Value.ToString() : string.Empty,
                Remark = patient.Remark,
                CreateDateDisplay = patient.CreateOn.ToString(GeneralUtility.LongDateTimeFormat),
                IsNewData = false
            };
        }

        private RelayCommand<IEnumerable<object>> _deletePatientCommand;

        /// <summary>
        /// Gets the DeletePatientCommand.
        /// </summary>
        public RelayCommand<IEnumerable<object>> DeletePatientCommand
        {
            get
            {
                return _deletePatientCommand ?? (_deletePatientCommand = new RelayCommand<IEnumerable<object>>(
                    ExecuteDeletePatientCommand,
                    CanExecuteDeletePatientCommand));
            }
        }

        private void ExecuteDeletePatientCommand(IEnumerable<object> parameter)
        {
            var selectedPatients = parameter.OfType<PatientBasicInfoDisplayViewModel>();

            // Show a message to confirm whether user needs to delete the selected data.
            var dialogMessage = new DialogMessage(string.Format("确认删除所选 {0} 条被测者信息以及所有相关的测试数据？该操作将不可恢复！",
                selectedPatients.Count()), dialogResult =>
            {
                // This is the CallBack of the DialogMessage.
                if (dialogResult == MessageBoxResult.Yes)
                {
                    try
                    {
                        DeleteSelectedPatientDataFromDataSource(selectedPatients);
                        RemoveSelectedPatientFromCollection(selectedPatients);
                        TestResultList.Clear();
                    }
                    catch (Exception ex)
                    {
                        RequestDisplayErrorMessage(ex.Message);
                    }
                }
            })
            {
                Button = MessageBoxButton.YesNo,
                Caption = "删除被测者信息",
                Icon = MessageBoxImage.Question
            };

            Messenger.Default.Send<DialogMessage>(dialogMessage, DeleteDataConfirmMessageToken);
        }

        private bool CanExecuteDeletePatientCommand(IEnumerable<object> parameter)
        {
            if (parameter == null || parameter.Count() == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private RelayCommand<IEnumerable<object>> _deleteTestResultCommand;

        /// <summary>
        /// Gets the DeleteTestResultCommand.
        /// </summary>
        public RelayCommand<IEnumerable<object>> DeleteTestResultCommand
        {
            get
            {
                return _deleteTestResultCommand ?? (_deleteTestResultCommand = new RelayCommand<IEnumerable<object>>(
                    ExecuteDeleteTestResultCommand,
                    CanExecuteDeleteTestResultCommand));
            }
        }

        private void ExecuteDeleteTestResultCommand(IEnumerable<object> parameter)
        {
            var selectedTestResults = parameter.OfType<AllTestResultsViewModel>();

            // Show a message to confirm whether user needs to delete the selected data.
            var dialogMessage = new DialogMessage(string.Format("确认删除所选 {0} 条测试数据？该操作将不可恢复！",
                selectedTestResults.Count()), dialogResult =>
            {
                // This is the CallBack of the DialogMessage.
                if (dialogResult == MessageBoxResult.Yes)
                {
                    try
                    {
                        DeleteSelectedTestDataFromDataSource(selectedTestResults);
                        RemoveSelectedTestResultFromCollection(selectedTestResults);
                    }
                    catch (Exception ex)
                    {
                        RequestDisplayErrorMessage(ex.Message);
                    }
                }
            })
            {
                Button = MessageBoxButton.YesNo,
                Caption = "删除测试数据",
                Icon = MessageBoxImage.Question
            };

            Messenger.Default.Send<DialogMessage>(dialogMessage, DeleteDataConfirmMessageToken);
        }

        private bool CanExecuteDeleteTestResultCommand(IEnumerable<object> parameter)
        {
            if (parameter == null || parameter.Count() == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private RelayCommand _goToNextCommand;

        /// <summary>
        /// Gets the GoToNextCommand.
        /// </summary>
        public RelayCommand GoToNextCommand
        {
            get
            {
                return _goToNextCommand
                    ?? (_goToNextCommand = new RelayCommand(
                                          () =>
                                          {
                                              // Send to PatientDataSearchPage, request go to next page.
                                              SelectedPatient.IsNewData = false;
                                              Messenger.Default.Send<PatientBasicInfoDisplayViewModel>(SelectedPatient, GoToNextPageMessageToken);
                                          },
                                          () => SelectedPatient == null ? false : true));
            }
        }

        /// <summary>
        /// Delete selected Patient and all related Testing data from data source.
        /// </summary>
        private void DeleteSelectedPatientDataFromDataSource(IEnumerable<PatientBasicInfoDisplayViewModel> selectedPatients)
        {
            var removedPatients = selectedPatients.Select(p => new Patient { Number = p.Number });
            _patientService.Delete(removedPatients);
        }

        /// <summary>
        /// Remove selected patient form collection.
        /// </summary>
        private void RemoveSelectedPatientFromCollection(IEnumerable<PatientBasicInfoDisplayViewModel> selectedPatients)
        {
            // Remove selected patient.
            PatientList = new ObservableCollection<PatientBasicInfoDisplayViewModel>(PatientList.Where(p => !selectedPatients.Contains(p)));
        }

        /// <summary>
        /// Delete selected Testing data from data source.
        /// </summary>
        private void DeleteSelectedTestDataFromDataSource(IEnumerable<AllTestResultsViewModel> selectedTestResults)
        {
            var removedTestResults = selectedTestResults.Select(t => new TestTracking { TestTimestamp = t.TestTimestamp });
            _testTrackingService.Delete(removedTestResults);
        }

        /// <summary>
        /// Remove selected test result form collection.
        /// </summary>
        private void RemoveSelectedTestResultFromCollection(IEnumerable<AllTestResultsViewModel> selectedTestResults)
        {
            // Remove selected test result.
            TestResultList = new ObservableCollection<AllTestResultsViewModel>(TestResultList.Where(t => !selectedTestResults.Contains(t)));
        }

        /// <summary>
        /// Load test result data.
        /// </summary>
        private void LoadTestResult()
        {
            TestResultList.Clear();

            var testTrackings = _testTrackingService.SearchByTestDateType(SelectedPatient.Number, (TestDateType)_selectedTestDateType);

            for (int i = 0; i < testTrackings.Count; i++)
            {
                var allTestResultsViewModel = new AllTestResultsViewModel(null)
                {
                    SequenceNumber = i + 1,
                    TestTimestamp = testTrackings[i].TestTimestamp,
                    AttendingDoctor = new AttendingDoctor { Name = testTrackings[i].AttendingDoctorName },
                    AttachedDiagnosticMessage = testTrackings[i].AttachedDiagnosticMessage,
                    TestDateDisplay = testTrackings[i].TestDate.ToString(GeneralUtility.LongDateTimeFormat),
                    DiagnosticMessageList = from t in testTrackings[i].TestResultDiagnosticMessages
                                            select new DiagnosticMessageReference { MessageContent = t.MessageContent }
                };

                ViewModelHelper.InitializeAllTestProperties(allTestResultsViewModel, testTrackings[i]);

                // Filter out all customized test items.
                var customizedTestItems = testTrackings[i].TestResults.Where(p => GeneralUtility.IsPositiveInteger(p.TestItemName));
                var customizedTestItemService = ServiceFactory.CreateCustomizedTestItemService();

                // Add to TestResultList based on AllCustomizedTestItems. For DataGrid Column Binding ONLY.
                foreach (var item in AllCustomizedTestItems)
                {
                    var matchedItem = customizedTestItems.Where(p => p.TestItemName == item.ItemTimestamp).FirstOrDefault();

                    if (matchedItem != null)
                    {
                        allTestResultsViewModel.TestItemList.Add(ViewModelHelper.ConvertFromTestResult(matchedItem,
                        ViewModelHelper.CreateCustomizedTestItemViewModel(customizedTestItemService.Get(matchedItem.TestItemName))));

                        allTestResultsViewModel.SelectedCustomizedTestItemList.Add(item);
                    }
                    else
                    {
                        allTestResultsViewModel.TestItemList.Add(new SingleTestItemViewModel(null));
                    }
                }

                TestResultList.Add(allTestResultsViewModel);
            }
        }
    }
}
